#include "StdAfx.h"
#include "ListImUI.h"

namespace DuiLib
{
	const int kIMListItemNormalHeight = 32;
	const int kIMListItemSelectedHeight = 50;
	const TCHAR* kIMListViewFile = _T("im_list_view.xml");

	CListIMUI::CListIMUI(CPaintManagerUI& paint_manager)
		: root_node_(NULL)
		, delay_deltaY_(0)
		, delay_number_(0)
		, delay_left_(0)
		, level_expand_image_(_T("<i list_icon_b.png>"))
		, level_collapse_image_(_T("<i list_icon_b.png>"))
		, level_text_start_pos_(0)
		, text_padding_(10, 0, 0, 0)
		, paint_manager_(paint_manager)
	{
		SetItemShowHtml(true);

		root_node_ = new UIListNode;
		root_node_->GetData().level_ = -1;
		root_node_->GetData().child_visible_ = true;
		root_node_->GetData().has_child_ = true;
		root_node_->GetData().list_element_ = NULL;
	}

	CListIMUI::~CListIMUI(void)
	{
		if (root_node_)
			delete root_node_;

		root_node_ = NULL;
	}

	bool CListIMUI::Add(CControlUI* pControl)
	{
		if (!pControl) return false;

		if (_tcsicmp(pControl->GetClass(), _T("ListContainerElementUI")) != 0)
			return false;

		return CListUI::Add(pControl);
	}

	bool CListIMUI::AddAt(CControlUI* pControl, int index)
	{
		if (!pControl) return false;

		if (_tcsicmp(pControl->GetClass(), _T("ListContainerElementUI")) != 0)
			return false;

		return CListUI::AddAt(pControl, index);
	}

	bool CListIMUI::Remove(CControlUI* pControl)
	{
		if (!pControl)
			return false;

		if (_tcsicmp(pControl->GetClass(), _T("ListContainerElementUI")) != 0)
			return false;

		if (reinterpret_cast<UIListNode*>(static_cast<CListContainerElementUI*>(pControl->GetInterface(_T("ListContainerElement")))->GetTag()) == NULL)
			return CListUI::Remove(pControl);
		else
			return RemoveNode(reinterpret_cast<UIListNode*>(static_cast<CListContainerElementUI*>(pControl->GetInterface(_T("ListContainerElement")))->GetTag()));
	}

	bool CListIMUI::RemoveAt(int index)
	{
		return Remove(GetItemAt(index));
	}

	void CListIMUI::RemoveAll()
	{
		CListUI::RemoveAll();

		for (int i = 0; i < root_node_->GetChildrenCount(); ++i)
			RemoveNode(root_node_->GetChild(i));
		delete root_node_;

		root_node_ = new UIListNode;
		root_node_->GetData().level_ = -1;
		root_node_->GetData().child_visible_ = true;
		root_node_->GetData().has_child_ = true;
		root_node_->GetData().list_element_ = NULL;
	}

	void CListIMUI::DoEvent(TEventUI& event)
	{
		if (!IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND)
		{
			if (m_pParent != NULL)
				m_pParent->DoEvent(event);
			else
				CVerticalLayoutUI::DoEvent(event);
			return;
		}

		if (event.Type == UIEVENT_TIMER && event.wParam == SCROLL_TIMERID)
		{
			if (delay_left_ > 0)
			{
				--delay_left_;
				SIZE sz = GetScrollPos();
				LONG lDeltaY = (LONG)(CalculateDelay((double)delay_left_ / delay_number_) * delay_deltaY_);
				if ((lDeltaY > 0 && sz.cy != 0) || (lDeltaY < 0 && sz.cy != GetScrollRange().cy))
				{
					sz.cy -= lDeltaY;
					SetScrollPos(sz);
					return;
				}
			}
			delay_deltaY_ = 0;
			delay_number_ = 0;
			delay_left_   = 0;
			m_pManager->KillTimer(this, SCROLL_TIMERID);
			return;
		}

		if (event.Type == UIEVENT_SCROLLWHEEL)
		{
			LONG lDeltaY = 0;
			if (delay_number_ > 0)
				lDeltaY =  (LONG)(CalculateDelay((double)delay_left_ / delay_number_) * delay_deltaY_);
			switch (LOWORD(event.wParam))
			{
			case SB_LINEUP:
				if (delay_deltaY_ >= 0)
					delay_deltaY_ = lDeltaY + 8;
				else
					delay_deltaY_ = lDeltaY + 12;
				break;
			case SB_LINEDOWN:
				if (delay_deltaY_ <= 0)
					delay_deltaY_ = lDeltaY - 8;
				else
					delay_deltaY_ = lDeltaY - 12;
				break;
			}
			if
				(delay_deltaY_ > 100) delay_deltaY_ = 100;
			else if
				(delay_deltaY_ < -100) delay_deltaY_ = -100;

			delay_number_ = (DWORD)sqrt((double)abs(delay_deltaY_)) * 5;
			delay_left_ = delay_number_;
			m_pManager->SetTimer(this, SCROLL_TIMERID, 50U);
			return;
		}
		CListUI::DoEvent(event);
	}

	UIListNode* CListIMUI::GetRoot()
	{
		return root_node_;
	}

	const TCHAR* const kLogoButtonControlName = _T("logo");
	const TCHAR* const kLogoContainerControlName = _T("logo_container");
	const TCHAR* const kNickNameControlName = _T("nickname");
	const TCHAR* const kDescriptionControlName = _T("description");
	const TCHAR* const kOperatorPannelControlName = _T("operation");

	static bool OnLogoButtonEvent(void* event)
	{
		if (((TEventUI*)event)->Type == UIEVENT_BUTTONDOWN)
		{
			CControlUI* pButton = ((TEventUI*)event)->pSender;
			if (pButton != NULL)
			{
				CListContainerElementUI* pListElement = (CListContainerElementUI*)(pButton->GetTag());
				if (pListElement != NULL)
					pListElement->DoEvent(*(TEventUI*)event);
			}
		}
		return true;
	}

	UIListNode* CListIMUI::AddNode(const ListIMItemInfo& item, UIListNode* parent /* = NULL */)
	{
		if (!parent)
			parent = root_node_;

		TCHAR szBuf[MAX_PATH] = {0};

		CListContainerElementUI* pListElement = NULL;
		if (!m_dlgBuilder.GetMarkup()->IsValid())
			pListElement = static_cast<CListContainerElementUI*>(m_dlgBuilder.Create(kIMListViewFile, (UINT)0, NULL, &paint_manager_));
		else
			pListElement = static_cast<CListContainerElementUI*>(m_dlgBuilder.Create((UINT)0, &paint_manager_));

		if (pListElement == NULL)
			return NULL;

		UIListNode* node = new UIListNode;

		node->GetData().level_ = parent->GetData().level_ + 1;
		if (item.folder)
			node->GetData().has_child_ = !item.empty;
		else
			node->GetData().has_child_ = false;

		node->GetData().folder_ = item.folder;

		node->GetData().child_visible_ = (node->GetData().level_ == 0);

		node->GetData().text_ = item.title;
		node->GetData().value_ = item.id;
		node->GetData().list_element_ = pListElement;

		if (!parent->GetData().child_visible_)
			pListElement->SetVisible(false);

		if (parent != root_node_ && !parent->GetData().list_element_->IsVisible())
			pListElement->SetVisible(false);

		CDuiRect rcPadding = text_padding_;
		rcPadding.left += level_text_start_pos_ * node->GetData().level_;
		pListElement->SetPadding(rcPadding);

		CButtonUI* log_button = static_cast<CButtonUI*>(paint_manager_.FindSubControlByName(pListElement, kLogoButtonControlName));
		if (log_button != NULL)
		{
			if (!item.folder && !item.image.empty())
			{
				_stprintf_s(szBuf, MAX_PATH - 1, _T("%s"), item.image.c_str());
				log_button->SetNormalImage(szBuf);
			}
			else
			{
				CContainerUI* logo_container = static_cast<CContainerUI*>(paint_manager_.FindSubControlByName(pListElement, kLogoContainerControlName));
				if (logo_container != NULL)
					logo_container->SetVisible(false);
			}
			log_button->SetTag((UINT_PTR)pListElement);
			log_button->OnEvent += MakeDelegate(&OnLogoButtonEvent);
		}

		TString html_text;
		if (node->GetData().has_child_)
		{
			if (node->GetData().child_visible_)
				html_text += level_expand_image_;
			else
				html_text += level_collapse_image_;

			_stprintf_s(szBuf, MAX_PATH - 1, _T("<X %d>"), level_text_start_pos_);
			html_text += szBuf;
		}

		if (item.folder)
			html_text += node->GetData().text_;
		else
		{
			_stprintf_s(szBuf, MAX_PATH - 1, _T("%s"), item.title.c_str());
			html_text += szBuf;
		}

		CLabelUI* nick_name = static_cast<CLabelUI*>(paint_manager_.FindSubControlByName(pListElement, kNickNameControlName));
		if (nick_name != NULL)
		{
			if (item.folder)
				nick_name->SetFixedWidth(0);
			nick_name->SetShowHtml(true);
			nick_name->SetText(html_text.c_str());
		}
		
		if (!item.folder && !item.subtitle.empty())
		{
			CLabelUI* description = static_cast<CLabelUI*>(paint_manager_.FindSubControlByName(pListElement, kDescriptionControlName));
			if (description != NULL)
			{
				_stprintf_s(szBuf, MAX_PATH - 1, _T("<x 20><c #808080>%s</c>"), item.subtitle.c_str());
				description->SetShowHtml(true);
				description->SetText(szBuf);
			}
		}

		pListElement->SetFixedHeight(kIMListItemNormalHeight);
		pListElement->SetTag((UINT_PTR)node);
		int index = 0;
		if (parent->HasChildren())
		{
			UIListNode* prev = parent->GetLastChild();
			index = prev->GetData().list_element_->GetIndex() + 1;
		}
		else
		{
			if (parent == root_node_)
				index = 0;
			else
				index = parent->GetData().list_element_->GetIndex() + 1;
		}
		if (!CListUI::AddAt(pListElement, index))
		{
			delete pListElement;
			delete node;
			node = NULL;
		}
		parent->AddChild(node);
		return node;
	}

	bool CListIMUI::RemoveNode(UIListNode* node)
	{
		if (!node || node == root_node_)
			return false;

		for (int i = 0; i < node->GetChildrenCount(); ++i)
			RemoveNode(node->GetChild(i));
		
		CListUI::Remove(node->GetData().list_element_);
		node->GetParent()->RemoveChild(node);
		delete node;

		return true;
	}

	void CListIMUI::SetChildVisible(UIListNode* node, bool visible)
	{
		if (!node || node == root_node_) return;

		if (node->GetData().child_visible_ == visible) return;

		node->GetData().child_visible_ = visible;

		TCHAR szBuf[MAX_PATH] = {0};
		TString html_text;
		if (node->GetData().has_child_)
		{
			if (node->GetData().child_visible_)
				html_text += level_expand_image_;
			else
				html_text += level_collapse_image_;

			_stprintf_s(szBuf, MAX_PATH - 1, _T("<x %d>"), level_text_start_pos_);
			html_text += szBuf;
			html_text += node->GetData().text_;

			CLabelUI* nick_name = static_cast<CLabelUI*>(paint_manager_.FindSubControlByName(node->GetData().list_element_, kNickNameControlName));
			if (nick_name != NULL)
			{
				nick_name->SetShowHtml(true);
				nick_name->SetText(html_text.c_str());
			}
		}

		if (!node->GetData().list_element_->IsVisible())
			return;

		if (!node->HasChildren())
			return;

		UIListNode* begin = node->GetChild(0);
		UIListNode* end = node->GetLastChild();
		for (int i = begin->GetData().list_element_->GetIndex(); i <= end->GetData().list_element_->GetIndex(); ++i)
		{
			CControlUI* control = GetItemAt(i);
			if (_tcsicmp(control->GetClass(), _T("ListContainerElementUI")) == 0)
			{
				if (visible)
				{
					UIListNode* local_parent = ((UIListNode*)control->GetTag())->GetParent();
					if (local_parent->GetData().child_visible_ && local_parent->GetData().list_element_->IsVisible())
						control->SetVisible(true);
				}
				else
					control->SetVisible(false);
			}
		}
	}

	bool CListIMUI::CanExpand(UIListNode* node) const
	{
		if (!node || node == root_node_) return false;
		return node->GetData().has_child_;
	}

	bool CListIMUI::SelectItem(int index, bool bTakeFocus /* = false */)
	{
		if (index == m_iCurSel) return true;

		// We should first unselect the currently selected item
		if (m_iCurSel >= 0)
		{
			CControlUI* pControl = GetItemAt(m_iCurSel);
			if (pControl != NULL)
			{
				IListItemUI* pListItem = static_cast<IListItemUI*>(pControl->GetInterface(_T("ListItem")));
				if (pListItem != NULL)
				{
					CListContainerElementUI* pFriendListItem = static_cast<CListContainerElementUI*>(pControl);
					UIListNode* node = (UIListNode*)pControl->GetTag();
					if ((pFriendListItem != NULL) && (node != NULL) && !node->folder())
					{
						pFriendListItem->SetFixedHeight(kIMListItemNormalHeight);
						CContainerUI* pOperatorPannel = static_cast<CContainerUI*>(paint_manager_.FindSubControlByName(pFriendListItem, kOperatorPannelControlName));
						if (pOperatorPannel != NULL)
							pOperatorPannel->SetVisible(false);
					}
					pListItem->Select(false);
				}
			}
			m_iCurSel = -1;
		}

		if (index < 0) return false;

		if (!__super::SelectItem(index, bTakeFocus))
			return false;

		CControlUI* pControl = GetItemAt(m_iCurSel);
		if (pControl != NULL)
		{
			CListContainerElementUI* pFriendListItem = static_cast<CListContainerElementUI*>(pControl);
			UIListNode* node = (UIListNode*)pControl->GetTag();
			if ((pFriendListItem != NULL) && (node != NULL) && !node->folder())
			{
				pFriendListItem->SetFixedHeight(kIMListItemSelectedHeight);
				CContainerUI* pOperatorPannel = static_cast<CContainerUI*>(paint_manager_.FindSubControlByName(pFriendListItem, kOperatorPannelControlName));
				if (pOperatorPannel != NULL)
					pOperatorPannel->SetVisible(true);
			}
		}
		return true;
	}
}